import os.path
import json
from scipy.fftpack import fft
import time
from threading import Thread
from PyQt5.QtWidgets import QWidget,QFileDialog
from PyQt5.QtGui import QIntValidator,QRegExpValidator
from PyQt5.QtCore import QRegExp,pyqtSignal
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
from matplotlib.figure import Figure
from gui.windows.dataWin import Ui_dataWindow
from config.method import *
from data.h5_wr import save_profile_data,save_analysis_data
from data.trs_wr import *
from data.align_tsdm import Align_TSDM
from data.align_static import *
from data.filter import *
from data.reduce_dim import *
from data.pre_2o import traces_2O_pre

class DataWindow(QWidget, Ui_dataWindow):

    signal_update_process_tips = pyqtSignal(str,bool,bool)
    signal_update_process_bar = pyqtSignal(float)
    signal_get_h5_path = pyqtSignal(str)
    signal_widget_unable=pyqtSignal()

    def __init__(self,startWindow):
        super(DataWindow, self).__init__()
        self.setupUi(self)
        self.setFixedSize(1252,942)
        self.startWindow=startWindow
        self.param_dict=startWindow.param_dict
        self.initial_property()
        self.initial_widget()
        self.connect_slot()

    def initial_property(self):
        self.text_string = ''
        self.header = None
        self.traces = None
        self.data = None
        self.plaintext = None
        self.ciphertext = None
        self.key = None
        self.midvalue = None
        self.profile_trs_start = 0
        self.profile_trs_end = 0
        self.traces_show = None
        self.process_log=[]
        self.real_mv_imp_file=None
        self.guess_mv_imp_file=None
        self.rkey_imp_file=None
        self.figure = Figure()
        self.ax = self.figure.add_subplot(111)
        self.canvas = FigureCanvasQTAgg(self.figure)
        self.toolbar = NavigationToolbar2QT(self.canvas, self)
        self.hL_traces_show.addWidget(self.canvas)
        self.hL_traces_show_toorbar.addWidget(self.toolbar)

    def initial_widget(self):
        self.plaintext_random.setChecked(self.param_dict['trs_plaintext_status']=='random')
        self.plaintext_unknow.setChecked(self.param_dict['trs_plaintext_status']=='unknow')
        self.plaintext_range_start.setText(str(self.param_dict['trs_plaintext_range'][0]))
        self.plaintext_range_end.setText(str(self.param_dict['trs_plaintext_range'][1]))
        self.ciphertext_random.setChecked(self.param_dict['trs_ciphertext_status']=='random')
        self.ciphertext_unknow.setChecked(self.param_dict['trs_ciphertext_status']=='unknow')
        self.ciphertext_range_start.setText(str(self.param_dict['trs_ciphertext_range'][0]))
        self.ciphertext_range_end.setText(str(self.param_dict['trs_ciphertext_range'][1]))
        self.key_fixed.setChecked(self.param_dict['trs_key_status']=='fixed')
        self.key_random.setChecked(self.param_dict['trs_key_status']=='random')
        self.key_unknow.setChecked(self.param_dict['trs_key_status']=='unknow')
        self.key_range_start.setText(str(self.param_dict['trs_key_range'][0]))
        self.key_range_end.setText(str(self.param_dict['trs_key_range'][1]))
        self.key_fixed_value.setText(' '.join(self.param_dict['fixed_key']))
        self.profile_trs_range_start.setText(str(self.param_dict['trs_profile_range'][0]))
        self.profile_trs_range_end.setText(str(self.param_dict['trs_profile_range'][1]))
        self.analysis_trs_range_start.setText(str(self.param_dict['trs_analysis_range'][0]))
        self.analysis_trs_range_end.setText(str(self.param_dict['trs_analysis_range'][1]))
        self.target_byte.setMaximum(get_mkey_bytes[self.param_dict["target_algorithm"]] - 1)
        self.target_byte.setValue(self.param_dict["target_byte"])
        self.set_plaintext_status()
        self.set_ciphertext_status()
        self.set_key_status()
        self.set_mv_gen_status()
        self.process_method.addItems(['仅加载', '高阶预处理', '切割', '降维', '对齐', '滤波','CGAN能量迹扩充','归一化','FFT', '保存中间值','清空'])
        self.align_algorithm.addItems(['欧式距离','相关系数','TSDM'])
        self.filter_algorithm.addItems(['小波变换','低通滤波','带通滤波'])
        self.redim_algorithm.addItems(['相关系数','PCA','LDA','信噪比','自编码器'])
        self.set_process_method('仅加载')
        self.progressBar.setRange(0, 100)
        self.rawdata_path.setText(self.param_dict['raw_data_path'])
        self.progressBar.hide()
        try:
            style_path = self.param_dict['proj_root'] + '/gui/resource/style.qss'
            with open(style_path, encoding='UTF-8') as f:
                self.setStyleSheet(f.read())
            self.header = read_header(self.param_dict['raw_data_path'])
            gen = read_trsfile(self.header, end_pos=10)
            _, self.traces_show, __ = next(gen)
            self.traces_show_index.setMaximum(self.traces_show.shape[0])
            self.update_traces_show()
            gen.close()
            self.update_text(self.header)
        except IOError as e:
            self.update_text(repr(e),False)
            self.update_text("加载原始数据失败！",False)
        self.set_validator()

    def connect_slot(self):
        self.process_method.currentTextChanged.connect(self.set_process_method)
        self.plaintext_random.clicked.connect(self.set_plaintext_status)
        self.plaintext_unknow.clicked.connect(self.set_plaintext_status)
        self.ciphertext_random.clicked.connect(self.set_ciphertext_status)
        self.ciphertext_unknow.clicked.connect(self.set_ciphertext_status)
        self.key_fixed.clicked.connect(self.set_key_status)
        self.key_random.clicked.connect(self.set_key_status)
        self.key_unknow.clicked.connect(self.set_key_status)
        self.rawdata_path_browse.clicked.connect(self.browse_rawdata_path)
        self.target_byte.valueChanged.connect(self.set_target_byte)
        self.traces_show_index.valueChanged.connect(self.update_traces_show)
        self.process_data.clicked.connect(self.start_process)
        self.generate_profile_data.clicked.connect(self.generate_profile_dataset)
        self.generate_analysis_data.clicked.connect(self.generate_analysis_dataset)
        self.signal_update_process_tips.connect(self.update_process_tips)
        self.signal_update_process_bar.connect(self.update_process_bar)
        self.signal_get_h5_path.connect(self.get_path_saveh5)
        self.signal_widget_unable.connect(lambda :self.enable_switch(False))
        self.related_info.textChanged.connect(lambda: self.related_info.moveCursor(11))
        self.clear_related_info.clicked.connect(lambda :self.update_text('#',True))
        self.mv_path_browse.clicked.connect(self.browse_mvdata_path)
        self.mv_direct_gen.clicked.connect(self.set_mv_gen_status)
        self.mv_file_import.clicked.connect(self.set_mv_gen_status)

    def start_process(self):
        if self.header is None:
            self.header = read_header(self.param_dict['raw_data_path'])
        if self.plaintext_random.isChecked():
            start = int(self.plaintext_range_start.text())
            end = int(self.plaintext_range_end.text())
            if end > self.header['data_length'] or (end-start)!=get_plaintext_bytes[self.param_dict["target_algorithm"]]:
                self.update_process_tips("错误的明文范围！")
                return
            self.param_dict["trs_plaintext_range"]=[start,end]
        if self.ciphertext_random.isChecked():
            start = int(self.ciphertext_range_start.text())
            end = int(self.ciphertext_range_end.text())
            if end > self.header['data_length'] or (end-start)!=get_ciphertext_bytes[self.param_dict["target_algorithm"]]:
                self.update_process_tips("错误的密文范围！")
                return
            self.param_dict["trs_ciphertext_range"] = [start, end]
        if self.key_random.isChecked():
            start = int(self.key_range_start.text())
            end = int(self.key_range_end.text())
            if end > self.header['data_length']:
                self.update_process_tips("错误的密钥范围！")
                return
            self.param_dict["trs_key_range"] = [start, end]
        if self.key_fixed.isChecked():
            fixed_key_str=self.key_fixed_value.text()
            fixed_key_str = fixed_key_str.translate({ord(' '): None})
            fixed_key = ['0'] * 16
            fixed_key_list = [fixed_key_str[i:i + 2] for i in range(0, min(len(fixed_key_str), get_mkey_bytes[self.param_dict["target_algorithm"]]*2), 2)]
            for i in range(len(fixed_key_list)):
                fixed_key[i] = fixed_key_list[i]
            self.param_dict['fixed_key']=fixed_key
            self.key=np.array([int(k,16) for k in fixed_key],np.uint8)
        self.enable_switch(False)
        self.progressBar.setValue(0)
        method=self.process_method.currentText()
        if method=='仅加载':
            self.load_rawdata()
        elif method=='切割':
            self.split_traces()
        elif method=='对齐':
            process=True
            if self.traces is None:
                process=self.load_rawdata()
            if process:
                self.align_traces()
        elif method=='滤波':
            process = True
            if self.traces is None:
                process = self.load_rawdata()
            if process:
                self.filter_traces()
        elif method=='高阶预处理':
            self.th_pre_2o=Thread(target=self.pre_2o_th)
            self.th_pre_2o.start()
        elif method=='降维':
            self.redim_traces()
        elif method=='CGAN能量迹扩充':
            self.hide()
            self.startWindow.CGANWindow.show()
        elif method=='归一化':
            self.normalize()
        elif method=='FFT':
            self.fft()
        elif method=='保存中间值':
            self.save_mvdata()
        elif method=='清空':
            self.clear_data()

    def clear_data(self):
        del self.traces
        self.traces=None
        self.data=None
        self.plaintext=None
        self.ciphertext=None
        self.key=None
        self.midvalue=None
        self.signal_update_process_tips.emit("数据已清空.", False,True)

    def pre_2o_th(self):
        if self.traces is None:
            self.signal_update_process_tips.emit("请先加载数据！", False, True)
            return
        end = min(self.traces.shape[0], 1000)
        if self.midvalue is None:
            self.generate_midvalue(0, end)
            self.th_gen_mv.join()
        self.traces = traces_2O_pre(self.traces, self.midvalue)
        self.process_log.append(("高阶预处理"))
        self.signal_update_process_tips.emit("已完成预处理.", False, True)
    def normalize(self):
        if self.traces is None:
            self.signal_update_process_tips.emit("请先加载数据！", False, True)
            return
        trs_min=np.min(self.traces)
        trs_max=np.max(self.traces)
        self.traces=(self.traces-trs_min)/(trs_max-trs_min) # 把数据0-1之间
        self.process_log.append(("归一化"))
        self.signal_update_process_tips.emit("已完成归一化.", False, True)

    def fft_th(self):
        trs_min = np.min(self.traces)
        trs_max = np.max(self.traces)
        self.traces = (self.traces - trs_min) / (trs_max - trs_min)
        self.traces=2*self.traces-1
        self.traces = np.abs(fft(self.traces))[1:]
        self.process_log.append(("快速傅里叶变换"))
        self.signal_update_process_tips.emit("已完成快速傅里叶变换.", False, True)

    def fft(self):
        if self.traces is None:
            self.signal_update_process_tips.emit("请先加载数据！", False, True)
            return
        th_fft = Thread(target=self.fft_th)
        th_fft.start()

    def generate_midvalue_th(self,start=None,end=None,re_gen=False):
        start=0 if start is None else start
        end=self.traces.shape[0] if end is None else end
        self.signal_widget_unable.emit()
        try:
            if self.mv_direct_gen.isChecked() or re_gen:
                self.signal_update_process_tips.emit("开始生成中间值...", False, False)
                box_cls = type('box', (object,), {'plaintext': None, 'ciphertext': None, 'mkey': None, 'rkey':None, 'target_byte': 0})
                box = box_cls()
                if self.param_dict['trs_plaintext_status']=='random':
                    box.plaintext=self.plaintext[start:end]
                if self.param_dict['trs_ciphertext_status'] == 'random':
                    box.ciphertext = self.ciphertext[start:end]
                if self.param_dict['trs_key_status'] != 'unknow':
                    if len(self.key.shape)==1:
                        box.mkey = self.key.reshape(1,-1)
                    else:
                        box.mkey = self.key[start: end]
                else:
                    raise RuntimeError("缺少密钥！")
                if box.mkey.shape[1]!=(get_mkey_bytes[self.param_dict["target_algorithm"]]):
                    raise RuntimeError("错误的密钥字节数量！")
                func_get_rkey=get_round_key[self.param_dict['target_algorithm']](self.param_dict['leakage_model'])
                box.rkey=func_get_rkey(box)
                box.target_byte=self.param_dict['target_byte']
                func_gen_midvalue = get_real_midvalue[self.param_dict['target_algorithm']](self.param_dict['leakage_model'])
                midvalue=func_gen_midvalue(box)
            else:
                midvalue=self.guess_mv_imp_file[start:end]
            self.midvalue=midvalue
            self.signal_update_process_tips.emit("生成中间值成功.", False,False)
        except StopIteration as e:
            self.midvalue =None
            self.signal_update_process_tips.emit(repr(e), False,False)
            self.signal_update_process_tips.emit("生成中间值失败！", False,False)

    def generate_midvalue(self,start=None,end=None,re_gen=False):
        th_gen_mv=Thread(target=self.generate_midvalue_th,args=(start,end,re_gen))
        th_gen_mv.start()
        self.th_gen_mv=th_gen_mv

    def redim_getPOI(self):
        if self.redim_algo not in ['相关系数','信噪比']:
            return None
        poi=None
        trs_cal = min(int(1e6 / self.header['trace_points']), self.header['traces_num'])
        if self.traces is None:
            trs_generator = read_trsfile(self.header, trs_cal)
            self.data, self.traces, finish = next(trs_generator)
            trs_generator.close()
        self.signal_update_process_tips.emit('开始计算感兴趣点...', True, False)
        time.sleep(0.05)
        if self.redim_algo=='相关系数':
            self.generate_midvalue(0,trs_cal)
            self.th_gen_mv.join()
            if self.midvalue is None:
                return None
            poi=poi_corrcoef(self.traces[:trs_cal], self.redim_point,self.midvalue[:trs_cal])
        elif self.redim_algo=='信噪比':
            poi=poi_snr(self.traces[:trs_cal],self.redim_point)
        return poi

    def redim_load_data(self,poi=None):
        try:
            add_step = min(100, 1e4 / self.header['traces_num'])
            trs_generator = read_trsfile(self.header, 100)
            index, progressBar_value, finish= 0,0,False
            self.signal_update_process_tips.emit('开始加载数据...', False, False)
            point_num=self.header['trace_points'] if poi is None else self.redim_point
            self.traces = np.zeros((self.header['traces_num'], point_num), type(self.traces_show[0, 0]))
            self.data = np.zeros((self.header['traces_num'], self.header['data_length']), np.uint8)
            while not finish:
                data, traces, finish = next(trs_generator)
                self.data[index:index + data.shape[0]] = data
                if poi is not None:
                    traces=traces[:,poi]
                self.traces[index:index + traces.shape[0]] = traces
                progressBar_value += add_step
                self.signal_update_process_bar.emit(progressBar_value)
                index += 100
            self.signal_update_process_tips.emit('数据加载完毕.', True, False)
        except Exception as e:
            self.signal_update_process_tips.emit(repr(e), False, False)
            self.signal_update_process_tips.emit('数据加载失败！', False, True)

    def redim_traces_th(self):
        if self.redim_algo in ['相关系数','信噪比']:
            self.signal_update_process_tips.emit(f'开始基于{self.redim_algo}的感兴趣点选择...', False, False)
            poi=self.redim_getPOI()
            if poi is None:
                self.signal_update_process_tips.emit(f'基于{self.redim_algo}的感兴趣点选择失败！', False, True)
                return
            if self.traces.shape[0]<self.header['traces_num']:
                self.midvalue=None
                self.redim_load_data(poi)
                self.process_log = []
            else:
                self.traces=self.traces[:,poi]
            self.signal_update_process_tips.emit(f'基于{self.redim_algo}的感兴趣点选择完毕.', False, True)
            self.process_log.append(("降维",f"基于{self.redim_algo}的感兴趣点选择",f"选择点数：{self.redim_point}"))
        elif self.redim_algo=='PCA':
            if self.traces is None:
                self.redim_load_data()
                self.process_log = []
            self.signal_update_process_tips.emit('开始主成分分析降维...', False, False)
            self.traces=pca(self.traces,self.redim_point)
            self.signal_update_process_tips.emit('主成分分析降维完毕.', False, True)
            self.process_log.append(("降维", "主成分分析", f"目标点数：{self.redim_point}"))
        elif self.redim_algo=='LDA':
            try:
                if self.traces is None:
                    self.redim_load_data()
                    self.process_log = []
                    time.sleep(0.01)
                start = int(self.profile_trs_range_start.text())
                end = int(self.profile_trs_range_end.text())
                if end > self.traces.shape[0] or start >= end:
                    self.update_process_tips("请输入正确的曲线范围！")
                    return
                self.generate_midvalue(start, end)
                time.sleep(0.01)
                self.th_gen_mv.join()
                self.signal_update_process_tips.emit('开始线性判别分析降维...', False, False)
                midvalue=self.midvalue
                self.traces=lda(self.traces,self.redim_point,midvalue,(start,end))
                self.signal_update_process_tips.emit('线性判别分析降维完毕.', False, True)
                self.process_log.append(("降维", "线性判别分析", f"目标点数：{self.redim_point}"))
            except ValueError as e:
                self.signal_update_process_tips.emit(repr(e), False, False)
                self.signal_update_process_tips.emit('线性判别分析降维失败！', False, True)

    def redim_traces_check(self):
        max_point = self.header['trace_points'] if self.traces is None else self.traces.shape[1]
        if max_point<50:
            self.update_text("点数较少，无需降维！")
            return False
        try:
            point=int(self.redim_algorithm_point.text())
            if point<1 or point >= max_point:
                raise ValueError
        except Exception as e:
            self.update_text(repr(e), False)
            self.update_text(f"请在选取点数编辑框中输入一个小于{max_point}的正整数", False)
            return False
        self.redim_point = point
        return True

    def redim_traces(self):
        check_res=self.redim_traces_check()
        if check_res is False:
            self.update_process_tips("降维失败！")
            return
        self.redim_algo=self.redim_algorithm.currentText()
        if self.redim_algo=='自编码器':
            if self.traces is None:
                self.signal_update_process_tips.emit('请先加载数据！', False, True)
                return
            self.hide()
            self.startWindow.AutoencoderWindow.show()
            self.startWindow.AutoencoderWindow.target_dim.setText(self.redim_algorithm_point.text())
            return
        if self.redim_algo in ['相关系数','信噪比'] and self.traces is None:
            self.progressBar.show()
        th_redim = Thread(target=self.redim_traces_th)
        th_redim.start()

    def filter_traces_th(self):
        if hasattr(self,'th_load'):
            self.th_load.join()
        self.signal_widget_unable.emit()
        if self.filter_algo=='小波变换':
            try:
                self.signal_update_process_tips.emit("开始基于小波变换的滤波...", False, False)
                filter_param=float(self.filter_param)
                if filter_param<0.1 or filter_param >0.9:
                    raise ValueError("请在算法参数编辑框中输入一个大于等于0.1且小于等于0.9的小数！")
                for i in range(self.traces.shape[0]):
                    self.traces[i] = wavelet_transform(self.traces[i], filter_param)
                    self.signal_update_process_bar.emit(100 * i / self.traces.shape[0])
                self.signal_update_process_tips.emit("滤波完毕.", False,True)
                self.process_log.append(("滤波", "小波变换",f"滤波参数：{filter_param}"))
            except Exception as e:
                self.signal_update_process_tips.emit(repr(e), False,False)
                self.signal_update_process_tips.emit("小波变换滤波失败(请在算法参数编辑框中输入一个大于等于0.1且小于等于0.9的小数)！", False,True)
        elif self.filter_algo=='低通滤波':
            try:
                self.signal_update_process_tips.emit("开始低通滤波...", False, False)
                filter_param=self.filter_param.split(' ')
                order=int(filter_param[0])
                ncf=float(filter_param[1])
                if order<=0 or order>=10:
                    raise ValueError("滤波阶数为大于0且小于10的整数！")
                if ncf<=0 or ncf>=1:
                    raise ValueError("归一化截止频率为大于0且小于1的小数！")
                self.traces=lowpass_filter(self.traces,order,ncf)
                self.signal_update_process_tips.emit("滤波完毕.", False, True)
                self.process_log.append(("滤波", "低通滤波", f"滤波参数：{self.filter_param}"))
            except Exception as e:
                self.signal_update_process_tips.emit(repr(e), False, False)
                self.signal_update_process_tips.emit("低通滤波失败(请在算法参数编辑框中输入(滤波阶数(0-10) 归一化截止频率(0-1)))！", False, True)
        elif self.filter_algo == '带通滤波':
            try:
                self.signal_update_process_tips.emit("开始带通滤波...", False, False)
                filter_param = self.filter_param.split(' ')
                order = int(filter_param[0])
                ncf_start = float(filter_param[1])
                ncf_end = float(filter_param[2])
                if order <= 0 or order >= 10:
                    raise ValueError("滤波阶数为大于0且小于10的整数！")
                if ncf_start <= 0 or ncf_start >= 1 or ncf_end <= 0 or ncf_end >= 1:
                    raise ValueError("归一化截止频率为大于0且小于1的小数！")
                if ncf_start>=ncf_end:
                    raise ValueError("归一化起始截止频率应大于归一化终止截止频率！")
                self.traces = bandpass_filter(self.traces, order, [ncf_start,ncf_end])
                self.signal_update_process_tips.emit("滤波完毕.", False, True)
                self.process_log.append(("滤波", "带通滤波", f"滤波参数：{self.filter_param}"))
            except Exception as e:
                self.signal_update_process_tips.emit(repr(e), False, False)
                self.signal_update_process_tips.emit("带通滤波失败(请在算法参数编辑框中输入(滤波阶数(0-10) 归一化起始截止频率(0-1) 归一化终止截止频率(0-1)))！", False, True)

    def filter_traces(self):
        self.filter_algo=self.filter_algorithm.currentText()
        self.filter_param=self.filter_algorithm_param.text()
        th_filter = Thread(target=self.filter_traces_th)
        th_filter.start()

    def align_traces_th(self):
        if hasattr(self, 'th_load'):
            self.th_load.join()
        self.signal_widget_unable.emit()
        base_trace = self.traces[0]
        if self.align_alogorithm=='欧式距离':
            for i in range(1,self.traces.shape[0]):
                self.traces[i]=align_norm2(base_trace,self.traces[i])
                self.signal_update_process_bar.emit(100*i/self.traces.shape[0])
        elif self.align_alogorithm=='相关系数':
            for i in range(1, self.traces.shape[0]):
                self.traces[i] = align_corrcoef(base_trace, self.traces[i])
                self.signal_update_process_bar.emit(100*i/self.traces.shape[0])
        elif self.align_alogorithm=='TSDM':
            align_tsdm = Align_TSDM()
            self.traces = align_tsdm.align(self.traces,self.signal_update_process_bar)
        self.process_log.append(("对齐", self.align_alogorithm))
        self.signal_update_process_tips.emit("对齐完毕.",False,True)

    def align_traces(self):
        self.progressBar.show()
        self.progressBar.setValue(0)
        self.align_alogorithm=self.align_algorithm.currentText()
        th_align = Thread(target=self.align_traces_th)
        th_align.start()

    def split_range_check(self):
        try:
            start = int(self.split_range_start.text())
            end = int(self.split_range_end.text())
            if self.traces is None:
                if end > self.header['trace_points'] or start >= end:
                    raise ValueError(f"请输入正确切割范围(0-{self.header['trace_points']}，且start<end)")
            elif end > self.traces.shape[1] or start >= end:
                    raise ValueError(f"请输入正确切割范围(0-{self.traces.shape[1]}，且start<end)")
        except Exception as e:
            self.update_text(repr(e), False)
            return False
        self.traces_range = (start, end)
        return True

    def split_traces(self):
        check_res=self.split_range_check()
        if check_res is False:
            self.update_process_tips("分割失败！")
            return
        if self.traces is None:
            self.process_log = []
            self.progressBar.show()
            self.progressBar.setValue(0)
            self.tips="分割完毕."
            th_load = Thread(target=self.load_data_th)
            th_load.start()
            self.th_load = th_load
            self.process_log.append(("分割", str(self.traces_range)))
        else:
            self.traces = self.traces[:, self.traces_range[0]:self.traces_range[1]]
            self.update_process_tips("分割完毕.")
            self.process_log.append(("分割", str(self.traces_range)))

    def load_data_th(self):
        try:
            add_step = min(100, 1e4 / self.header['traces_num'])
            self.traces = np.zeros((self.header['traces_num'], self.traces_range[1]-self.traces_range[0]), type(self.traces_show[0, 0]))
            self.data = np.zeros((self.header['traces_num'], self.header['data_length']), np.uint8)
            trs_generator = read_trsfile(self.header, 100)
            index = 0
            progressBar_value = 0
            finish = False
            while not finish:
                data, traces, finish = next(trs_generator)
                self.data[index:index + data.shape[0]] = data
                self.traces[index:index + traces.shape[0]] = traces[:,self.traces_range[0]:self.traces_range[1]]
                progressBar_value += add_step
                self.signal_update_process_bar.emit(progressBar_value)
                index += 100
            self.signal_update_process_tips.emit(self.tips,True,True)
        except Exception as e:
            self.signal_update_process_tips.emit(repr(e), False, False)
            self.signal_update_process_tips.emit("原始数据加载失败!", False, True)

    def load_rawdata(self):
        self.progressBar.show()
        self.progressBar.setValue(0)
        self.traces_range=(0,self.header['trace_points'])
        self.tips="原始数据加载完毕."
        th_load = Thread(target=self.load_data_th)
        th_load.start()
        self.process_log = []
        self.th_load=th_load
        return True

    def set_metadata(self):
        if self.plaintext_random.isChecked():
            self.plaintext = self.data[:, int(self.plaintext_range_start.text()):int(self.plaintext_range_end.text())]
        else:
            self.plaintext=None
        if self.ciphertext_random.isChecked():
            self.ciphertext = self.data[:, int(self.ciphertext_range_start.text()):int(self.ciphertext_range_end.text())]
        else:
            self.ciphertext = None
        if self.key_random.isChecked():
            self.key = self.data[:, int(self.key_range_start.text()):int(self.key_range_end.text())]
        elif self.param_dict['trs_key_status']=='fixed':
            self.key = np.array([int(k, 16) for k in self.param_dict['fixed_key']], np.uint8)
        else:
            self.key = None

    def enable_switch(self,enable):
        self.process_data.setEnabled(enable)
        self.plaintext_random.setEnabled(enable)
        self.plaintext_unknow.setEnabled(enable)
        self.ciphertext_random.setEnabled(enable)
        self.ciphertext_unknow.setEnabled(enable)
        self.key_fixed.setEnabled(enable)
        self.key_random.setEnabled(enable)
        self.key_unknow.setEnabled(enable)
        self.plaintext_range_start.setEnabled(enable)
        self.plaintext_range_end.setEnabled(enable)
        self.ciphertext_range_start.setEnabled(enable)
        self.ciphertext_range_end.setEnabled(enable)
        self.key_range_start.setEnabled(enable)
        self.key_range_end.setEnabled(enable)
        self.generate_profile_data.setEnabled(enable)
        self.generate_analysis_data.setEnabled(enable)

    def gen_description(self,traces_shape,midvalue_shape,box=None):
        description={}
        if box is None:
            description["dataset_type"]="profile dataset"
        else:
            description["dataset_type"]="analysis dataset"
        description["target_algorithm"]=self.param_dict['target_algorithm']
        description["leakage_model"]=self.param_dict['leakage_model']
        description["target_byte"]=self.param_dict['target_byte']
        description["raw_data_path"]=self.param_dict['raw_data_path']
        if 'description' in self.header:
            description["raw_data_description"]=self.header['description']
        description["process_record"]=str(self.process_log).replace(',','，')
        description["traces_shape"]=str(traces_shape).replace(',','，')
        description["midvalue_shape"]=str(midvalue_shape).replace(',','，')
        if box is not None:
            if box.plaintext is not None:
                description["plaintext_shape"]=str(box.plaintext.shape).replace(',','，')
            if box.ciphertext is not None:
                description["ciphertext_shape"]=str(box.ciphertext.shape).replace(',','，')
            if box.mkey is not None:
                description["main_key_shape"]=str(box.mkey.shape).replace(',','，')
            if box.rkey is not None:
                description["round_key_shape"]=str(box.rkey.shape).replace(',','，')
        return str(description)

    def gen_profile_th(self):
        self.th_gen_mv.join()
        try:
            if self.midvalue is None:
                raise RuntimeError("缺少中间值！(可能需要密文或明文，但选择了密文未知或明文未知)")
            start = self.profile_trs_start
            end = self.profile_trs_end
            ctime = time.localtime()
            profile_dir = os.path.dirname(self.param_dict['profile_file_dir']) + f"/{ctime.tm_year}-{ctime.tm_mon}-{ctime.tm_mday}/"
            if os.path.exists(profile_dir) is False:
                os.makedirs(profile_dir)
            hms = f'{ctime.tm_hour:02d}{ctime.tm_min:02d}{ctime.tm_sec:02d}'
            file_name = f"{self.param_dict['target_algorithm']}_{self.param_dict['leakage_model']}_byte{self.param_dict['target_byte']}_{hms}.h5"
            self.save_continue = False
            self.signal_get_h5_path.emit(profile_dir + file_name)
            while self.save_continue is False:
                time.sleep(0.1)
            if self.path_saveh5:
                self.signal_update_process_tips.emit("开始生成建模数据集...", False, False)
                description=self.gen_description((end-start,self.traces.shape[1]),self.midvalue.shape)
                save_profile_data(self.path_saveh5,self.traces[start:end],self.midvalue,description)
                self.signal_update_process_tips.emit("生成建模数据集成功.", False, True)
            else:
                raise RuntimeError("未选择正确的路径！")
        except Exception as e:
            self.signal_update_process_tips.emit(repr(e), False,False)
            self.signal_update_process_tips.emit("生成建模数据集失败！", False,True)

    def generate_profile_dataset(self):
        if self.traces is None:
            self.update_process_tips("请先加载数据！")
            return
        if self.mv_file_import.isChecked() and self.real_mv_imp_file is None:
            self.enable_switch(False)
            self.browse_mvdata_path()
        start=int(self.profile_trs_range_start.text())
        end=int(self.profile_trs_range_end.text())
        if end > self.traces.shape[0] or start >= end:
            self.update_process_tips("请输入正确的曲线范围！")
            return
        if self.midvalue is not None and self.midvalue.shape[0]==self.traces.shape[0]:
            self.midvalue=self.midvalue[start:end]
        elif self.midvalue is not None and self.profile_trs_start <= start and self.profile_trs_end >= end and self.midvalue.shape[0]==self.profile_trs_end-self.profile_trs_start:
            self.midvalue=self.midvalue[start-self.profile_trs_start:end-self.profile_trs_start]
        else:
            self.generate_midvalue(start,end)
        self.profile_trs_start =start
        self.profile_trs_end =end
        self.enable_switch(False)
        th_profile=Thread(target=self.gen_profile_th)
        th_profile.start()

    def gen_analysis_th(self):
        try:
            start=self.analysis_trs_start
            end=self.analysis_trs_end
            if self.mv_direct_gen.isChecked():
                self.signal_update_process_tips.emit("开始生成猜测中间值...", False, False)
                box_cls = type('box', (object,), {'plaintext': None, 'ciphertext': None, 'mkey':None,'rkey':None, 'target_byte': 0})
                box = box_cls()
                if self.param_dict['trs_plaintext_status']=='random':
                    box.plaintext=self.plaintext[start:end]
                if self.param_dict['trs_ciphertext_status'] == 'random':
                    box.ciphertext = self.ciphertext[start:end]
                if self.param_dict['trs_key_status'] != 'unknow':
                    key=self.key
                    if len(self.key.shape)==1:
                        key = key.reshape(1,-1)
                    if key.shape[0]>1:
                        key=key[start:end]
                    mkey_bytes=get_mkey_bytes[self.param_dict["target_algorithm"]]
                    if key.shape[1] == mkey_bytes:
                        box.mkey = key
                        func_get_round_key = get_round_key[self.param_dict['target_algorithm']](self.param_dict['leakage_model'])
                        box.rkey = func_get_round_key(box)
                    elif key.shape[1] < mkey_bytes:
                        box.rkey =key
                    else:
                        box.mkey = key[:,mkey_bytes]
                        box.rkey=key[:,mkey_bytes:]
                box.target_byte=self.param_dict['target_byte']
                func_gen_midvalue = get_guess_midvalue[self.param_dict['target_algorithm']](self.param_dict['leakage_model'])
                midvalue=func_gen_midvalue(box)
            else:
                midvalue=self.guess_mv_imp_file[start:end]
            self.signal_update_process_tips.emit("生成猜测中间值成功.", False,False)
            ctime = time.localtime()
            analysis_dir=os.path.dirname(self.param_dict['analysis_file_dir'])+f"/{ctime.tm_year}-{ctime.tm_mon}-{ctime.tm_mday}/"
            if os.path.exists(analysis_dir) is False:
                os.makedirs(analysis_dir)
            hms=f'{ctime.tm_hour:02d}{ctime.tm_min:02d}{ctime.tm_sec:02d}'
            file_name=f"{self.param_dict['target_algorithm']}_{self.param_dict['leakage_model']}_byte{self.param_dict['target_byte']}_{hms}.h5"
            self.save_continue = False
            self.signal_get_h5_path.emit(analysis_dir + file_name)
            while self.save_continue is False:
                time.sleep(0.1)
            if self.path_saveh5:
                self.signal_update_process_tips.emit("开始生成分析数据集...", False, False)
                if self.mv_direct_gen.isChecked():
                    description=self.gen_description((end-start,self.traces.shape[1]),midvalue.shape,box)
                    save_analysis_data(self.path_saveh5, self.traces[start:end], midvalue, box.mkey, box.rkey, box.plaintext, box.ciphertext,description)
                else:
                    save_analysis_data(self.path_saveh5, self.traces[start:end], midvalue,rkey=self.rkey_imp_file)
                self.signal_update_process_tips.emit("生成分析数据集成功.", False, True)
            else:
                raise RuntimeError("未选择正确的路径！")
        except StopIteration as e:
            self.signal_update_process_tips.emit(repr(e), False,False)
            self.signal_update_process_tips.emit("生成分析数据集失败！", False,True)

    def generate_analysis_dataset(self):
        if self.traces is None:
            self.update_process_tips("请先加载数据！")
            return
        if self.mv_file_import.isChecked() and self.guess_mv_imp_file is None:
            self.enable_switch(False)
            self.browse_mvdata_path()
        self.analysis_trs_start=int(self.analysis_trs_range_start.text())
        self.analysis_trs_end=int(self.analysis_trs_range_end.text())
        if self.analysis_trs_end > self.traces.shape[0] or self.analysis_trs_start >= self.analysis_trs_end:
            self.update_process_tips("请输入正确的曲线范围！")
            return
        self.enable_switch(False)
        th_analysis = Thread(target=self.gen_analysis_th)
        th_analysis.start()

    def set_validator(self):
        if self.header is None:
            int_validator1 = QIntValidator(0, 32, self)
            int_validator2 = QIntValidator(0, 400, self)
            int_validator3 = QIntValidator(0, 60000, self)
        else:
            int_validator1 = QIntValidator(0, self.header['data_length'], self)
            int_validator2 = QIntValidator(0, self.header['trace_points'], self)
            int_validator3 = QIntValidator(0, self.header['traces_num'], self)
        self.plaintext_range_start.setValidator(int_validator1)
        self.plaintext_range_end.setValidator(int_validator1)
        self.ciphertext_range_start.setValidator(int_validator1)
        self.ciphertext_range_end.setValidator(int_validator1)
        self.key_range_start.setValidator(int_validator1)
        self.key_range_end.setValidator(int_validator1)
        self.split_range_start.setValidator(int_validator2)
        self.split_range_end.setValidator(int_validator2)
        self.redim_algorithm_point.setValidator(int_validator2)
        self.profile_trs_range_start.setValidator(int_validator3)
        self.profile_trs_range_end.setValidator(int_validator3)
        self.analysis_trs_range_start.setValidator(int_validator3)
        self.analysis_trs_range_end.setValidator(int_validator3)
        reg = QRegExp("[a-fA-F0-9\s]+$")
        reg_validator=QRegExpValidator()
        reg_validator.setRegExp(reg)
        self.key_fixed_value.setValidator(reg_validator)
        self.key_fixed_value.setMaxLength(get_mkey_bytes[self.param_dict["target_algorithm"]]*3-1)

    def update_process_tips(self,tip,set_data=False,finish=True):
        if len(tip)>3 and tip[-3:]=='...':
            tip='(Please Wait)'+tip
        elif tip[-1]=='.':
            tip='(Execution Success)'+tip
        elif tip[-1]=='！' or tip[-1]=='!':
            tip = '(Execution Fail)' + tip
        self.update_text(tip, False)
        if finish:
            self.update_traces_show(True)
            self.progressBar.hide()
            self.enable_switch(True)
        if set_data:
            self.set_metadata()

    def update_process_bar(self,value):
        self.progressBar.setValue(value)

    def update_traces_show(self,reset_show=False):
        overlap=self.traces_show_overlap.isChecked()
        if reset_show is True:
            if self.traces is None:
                return
            max_index=min(10, self.traces.shape[0])
            self.traces_show_index.setMaximum(max_index)
            self.traces_show = self.traces[:max_index].copy()
            self.traces_show_index.setValue(1)
            overlap=False
        if self.traces_show is None:
            return
        index=self.traces_show_index.value()-1
        if overlap:
            self.ax.plot(self.traces_show[index])
        else:
            self.figure.clear()
            self.ax = self.figure.add_subplot(111)
            self.ax.plot(self.traces_show[index])
        self.canvas.draw()

    def update_text(self,data,override=True):
        if override:
            self.text_string=''
        if type(data) is dict:
            self.text_string+='trs file head:\n'
            for k in data:
                self.text_string+=f"  {k}: {data[k]}\n"
            self.text_string+='\n'
        else:
            if data[-1]=='#':
                self.text_string+=str(data)[:-1]
            else:
                self.text_string += str(data)+'\n'
        self.related_info.setText(self.text_string)
        if len(self.text_string)>10240:
            self.text_string=self.text_string[5120:]

    def get_path_saveh5(self,pre_path):
        file_path = QFileDialog.getSaveFileName(self, "保存文件", pre_path, "h5文件(*.h5)")
        self.path_saveh5=file_path[0]
        self.save_continue=True

    def browse_rawdata_path(self):
        try:
            fname = QFileDialog.getOpenFileName(self, "Open Trs", self.param_dict['raw_data_path'], "*.trs")
            if fname[0]:
                relative_path = '../' + os.path.relpath(fname[0], start='../').replace('\\', '/')
                self.header = read_header(relative_path)
                self.param_dict['raw_data_path']=relative_path
                self.rawdata_path.setText(relative_path)
                self.traces=None
                self.plaintext=None
                self.ciphertext=None
                self.key=None
                self.data = None
                self.profile_trs_start = 0
                self.profile_trs_end = 0
                gen = read_trsfile(self.header, end_pos=10)
                _, self.traces_show, __ = next(gen)
                self.traces_show_index.setMaximum(self.traces_show.shape[0])
                self.update_traces_show()
                gen.close()
                self.update_text(self.header)
                self.set_validator()
        except Exception as e:
            self.update_text(repr(e), False)
            self.update_text("加载原始数据失败！",False)

    def browse_mvdata_path(self):
        try:
            fname = QFileDialog.getOpenFileName(self, "Open MV", self.param_dict['midvalue_file_dir'], "*.json")
            if fname[0]:
                relative_path = '../' + os.path.relpath(fname[0], start='../').replace('\\', '/')
                self.param_dict['mv_data_path']=relative_path
                self.mv_path.setText(relative_path)
                with open(relative_path, 'r') as f:
                    mv_dict = json.load(f)
                if 'real_intermediate_value' in mv_dict:
                    self.real_mv_imp_file=np.array(mv_dict['real_intermediate_value'])
                if 'guess_intermediate_value' in mv_dict:
                    self.guess_mv_imp_file=np.array(mv_dict['guess_intermediate_value'])
                if 'round_key' in mv_dict:
                    self.rkey_imp_file=mv_dict['round_key']
                if 'profile_traces_range' in mv_dict:
                    self.profile_trs_range_start.setText(str(mv_dict['profile_traces_range'][0]))
                    self.profile_trs_range_end.setText(str(mv_dict['profile_traces_range'][1]))
                if 'analysis_traces_range' in mv_dict:
                    self.analysis_trs_range_start.setText(str(mv_dict['analysis_traces_range'][0]))
                    self.analysis_trs_range_end.setText(str(mv_dict['analysis_traces_range'][1]))
                self.update_process_tips("加载中间值数据成功.")
        except Exception as e:
            self.update_text(repr(e), False)
            self.update_process_tips("加载中间值数据失败！")

    def save_mvdata(self):
        try:
            if self.traces is None:
                self.signal_update_process_tips.emit("请先加载数据！", False, True)
                return
            self.update_process_tips("开始生成中间值...", False, False)
            profile_start = int(self.profile_trs_range_start.text())
            profile_end = int(self.profile_trs_range_end.text())
            self.generate_midvalue(profile_start,profile_end,True)
            self.th_gen_mv.join()
            real_mv=self.midvalue
            box_cls = type('box', (object,), {'plaintext': None, 'ciphertext': None, 'mkey': None, 'rkey': None, 'target_byte': 0})
            box = box_cls()
            analysis_start = int(self.analysis_trs_range_start.text())
            analysis_end = int(self.analysis_trs_range_end.text())
            if self.param_dict['trs_plaintext_status'] == 'random':
                box.plaintext = self.plaintext[analysis_start:analysis_end]
            if self.param_dict['trs_ciphertext_status'] == 'random':
                box.ciphertext = self.ciphertext[analysis_start:analysis_end]
            if self.param_dict['trs_key_status'] != 'unknow':
                key = self.key
                if len(self.key.shape) == 1:
                    key = key.reshape(1, -1)
                if key.shape[0] > 1:
                    key = key[analysis_start:analysis_end]
                mkey_bytes = get_mkey_bytes[self.param_dict["target_algorithm"]]
                if key.shape[1] == mkey_bytes:
                    box.mkey = key
                    func_get_round_key = get_round_key[self.param_dict['target_algorithm']](self.param_dict['leakage_model'])
                    box.rkey = func_get_round_key(box)
                elif key.shape[1] < mkey_bytes:
                    box.rkey = key
                else:
                    box.mkey = key[:, mkey_bytes]
                    box.rkey = key[:, mkey_bytes:]
            box.target_byte = self.param_dict['target_byte']
            func_gen_midvalue = get_guess_midvalue[self.param_dict['target_algorithm']](self.param_dict['leakage_model'])
            guess_mv = func_gen_midvalue(box)
            ctime = time.localtime()
            dir = self.param_dict['midvalue_file_dir'] + f"{ctime.tm_year}-{ctime.tm_mon}-{ctime.tm_mday}/"
            hms = f'{ctime.tm_hour:02d}{ctime.tm_min:02d}{ctime.tm_sec:02d}'
            file_name = f"{self.param_dict['target_algorithm']}_{self.param_dict['leakage_model']}_byte{self.param_dict['target_byte']}_{hms}.json"
            if not os.path.exists(dir):
                os.makedirs(dir)
            fname, _ = QFileDialog.getSaveFileName(self, "Save MV", dir+file_name, "*.json")
            if len(np.array(box.rkey).shape)==1:
                rkey_byte=int(box.rkey[self.param_dict['target_byte']])
            else:
                rkey_byte=int(box.rkey[0,self.param_dict['target_byte']])
            if fname:
                self.update_process_tips("开始保存中间值...",False,False)
                midvalue_dict={
                    'real_intermediate_value':real_mv.tolist(),
                    'guess_intermediate_value':guess_mv.tolist(),
                    'round_key':rkey_byte,
                    'profile_traces_range':[profile_start,profile_end],
                    'analysis_traces_range':[analysis_start,analysis_end]
                }
                with open(fname, "w") as file:
                    json.dump(midvalue_dict, file, separators=(',',':'))
            self.update_process_tips("保存中间值数据成功.")
        except StopIteration as e:
            self.update_text(repr(e), False)
            self.update_process_tips("保存中间值数据失败！")

    def set_target_byte(self,target_byte):
        self.param_dict["target_byte"] = target_byte

    def set_process_method(self,method):
        switch_not_split=method != '切割'
        switch_not_align=method != '对齐'
        switch_not_filter=method != '滤波'
        switch_not_redim=method != '降维'
        self.split_range_.setHidden(switch_not_split)
        self.split_range_L.setHidden(switch_not_split)
        self.split_range_start.setHidden(switch_not_split)
        self.split_range_end.setHidden(switch_not_split)
        self.align_algorithm_L.setHidden(switch_not_align)
        self.align_algorithm.setHidden(switch_not_align)
        self.filter_algorithm.setHidden(switch_not_filter)
        self.filter_algorithm_L.setHidden(switch_not_filter)
        self.filter_algorithm_param.setHidden(switch_not_filter)
        self.filter_algorithm_param_L.setHidden(switch_not_filter)
        self.redim_algorithm.setHidden(switch_not_redim)
        self.redim_algorithm_L.setHidden(switch_not_redim)
        self.redim_algorithm_point.setHidden(switch_not_redim)
        self.redim_algorithm_point_L.setHidden(switch_not_redim)

    def set_plaintext_status(self):
        switch=self.plaintext_unknow.isChecked()
        if switch: self.param_dict['trs_plaintext_status']='unknow'
        else: self.param_dict['trs_plaintext_status']='random'
        self.plaintext_range_L.setHidden(switch)
        self.plaintext_range_start.setHidden(switch)
        self.plaintext_range_.setHidden(switch)
        self.plaintext_range_end.setHidden(switch)

    def set_ciphertext_status(self):
        switch = self.ciphertext_unknow.isChecked()
        if switch: self.param_dict['trs_ciphertext_status']='unknow'
        else: self.param_dict['trs_ciphertext_status']='random'
        self.ciphertext_range_L.setHidden(switch)
        self.ciphertext_range_start.setHidden(switch)
        self.ciphertext_range_.setHidden(switch)
        self.ciphertext_range_end.setHidden(switch)

    def set_key_status(self):
        switch_not_random = self.key_random.isChecked() is False
        switch_not_fixed = self.key_fixed.isChecked() is False
        if switch_not_fixed is False:
            self.param_dict['trs_key_status']='fixed'
            self.key=np.array([int(k,16) for k in self.param_dict['fixed_key']],np.uint8)
        elif switch_not_random is False:
            start=int(self.key_range_start.text())
            end=int(self.key_range_end.text())
            self.param_dict['trs_key_status']='random'
            if self.header is not None:
                if end>self.header['data_length'] or start>=end:
                    self.update_process_tips("错误的密钥范围！")
                elif self.data is not None:
                    self.key=self.data[:,start:end]
                else:
                    self.key=None
        else:
            self.param_dict['trs_key_status']='unknow'
            self.key=None
        self.key_range_L.setHidden(switch_not_random)
        self.key_range_start.setHidden(switch_not_random)
        self.key_range_.setHidden(switch_not_random)
        self.key_range_end.setHidden(switch_not_random)
        self.key_fixed_value.setHidden(switch_not_fixed)
        self.key_fixed_value_L.setHidden(switch_not_fixed)

    def set_mv_gen_status(self):
        swith_direct_gen=self.mv_direct_gen.isChecked()
        self.mv_path_L.setHidden(swith_direct_gen)
        self.mv_path.setHidden(swith_direct_gen)
        self.mv_path_browse.setHidden(swith_direct_gen)

    def showEvent(self, a0):
        self.target_byte.setMaximum(get_mkey_bytes[self.param_dict["target_algorithm"]] - 1)
        self.target_byte.setValue(self.param_dict["target_byte"])

    def closeEvent(self, a0):
        self.text_string=''
        self.enable_switch(True)
        self.clear_data()
        self.update_text("#",True)
        self.startWindow.fresh()
        self.startWindow.show()